/**
 * 有 n 个气球，编号为0 到 n - 1，每个气球上都标有一个数字，这些数字存在数组nums中。
 *
 * 现在要求你戳破所有的气球。戳破第 i 个气球，你可以获得nums[i - 1] * nums[i] * nums[i + 1] 枚硬币。 这里的 i - 1 和 i + 1 代表和i相邻的两个气球的序号。如果 i - 1或 i + 1 超出了数组的边界，那么就当它是一个数字为 1 的气球。
 *
 * 求所能获得硬币的最大数量。
 *
 *
 *
 * 链接：https://leetcode.cn/problems/burst-balloons
 * 题解：https://labuladong.gitee.io/algo/3/28/92/
 */
class MaxCoinsDp {
    /**
     * 迭代版动规
     * @param nums
     * @return
     */
    public int maxCoinsDp(int[] nums) {//dp[i][j]表示开区间i,j里戳破所有气球的最高分数，所以最后返回的是dp[0][n+1]，代表开区间0到n+1戳破气球的最高分数
        int n=nums.length;
        int[] points=new int[n+2];
        points[0]=points[n+1]=1;
        for(int i=1;i<=n;i++) {
            points[i]=nums[i-1];
        }
        int[][] dp=new int[n+2][n+2];
        for(int i=n;i>=0;i--) {
            for(int j=i+1;j<n+2;j++) {//从下往上遍历
                for(int k=i+1;k<j;k++) {//从左往右遍历，这里解释一下，因为dp[i][j]依赖dp[i][k]和dp[k][j]，所以k满足i<k<j，所以要从下往上，从左往右遍历
                    dp[i][j]=Math.max(dp[i][j],dp[i][k]+dp[k][j]+points[i]*points[k]*points[j]);
                }
            }
        }
        return dp[0][n+1];
    }

    /**
     * 递归版动规
     */
    int points[];
    int memo[][];    
    public int maxCoins(int[] nums) {//dp[i][j]表示开区间i,j里戳破所有气球的最高分数，所以最后返回的是dp[0][n+1]，代表开区间0到n+1戳破气球的最高分数
        int n=nums.length;
        points=new int[n+2];
        memo=new int[n+2][n+2];
        points[0]=points[n+1]=1;
        for(int i=1;i<=n;i++) {
            points[i]=nums[i-1];
        }
        for(int[] row:memo) {
            Arrays.fill(row,-1);
        }
        return dp(0,n+1);
    }
    public int dp(int left,int right) {
        if(left>=right-1) {//开区间，当left>=right-1，说明区间(left,right)之间已经没有数字可取
            return 0;
        }
        if(memo[left][right]!=-1) {
            return memo[left][right];
        }
        for(int i=left+1;i<right;i++) {
            int val=points[left]*points[i]*points[right];
            int sum=val+dp(left,i)+dp(i,right);
            memo[left][right]=Math.max(memo[left][right],sum);
        }
        return memo[left][right];
    }
}